package internal

import (
	"context"
	"encoding/json"
	"github.com/go-redis/redis/v8"
	"github.com/morgine/log"
)

type RedisStorage struct {
	client *redis.Client
	noCtx  context.Context
}

func NewRedisStorage(client *redis.Client) *RedisStorage {
	return &RedisStorage{client: client, noCtx: context.Background()}
}

func (r *RedisStorage) SaveFlow(coin string, flow *Flow) {
	data, err := json.Marshal(flow)
	if err != nil {
		log.Error.Println(err)
	} else {
		err = r.client.LPush(r.noCtx, coin, string(data)).Err()
		if err != nil {
			log.Error.Println(err)
		}
	}
}

func (r *RedisStorage) TotalFlows(coin string, maxLength int64) ([]*Flow, error) {
	total, err := r.client.LLen(r.noCtx, coin).Result()
	if err != nil {
		return nil, err
	} else {
		if total > maxLength {
			total = maxLength
		}
		var flows = make([]*Flow, total)
		idx := 0
		var start int64 = 0
		var limit int64 = 500
		for {
			stop := start + limit - 1
			if stop >= total {
				stop = total - 1
			}
			res, err := r.client.LRange(r.noCtx, coin, start, stop).Result()
			if err != nil && err != redis.Nil {
				return nil, err
			} else {
				if len(res) > 0 {
					for _, re := range res {
						flow := &Flow{}
						err = json.Unmarshal([]byte(re), flow)
						if err != nil {
							return nil, err
						} else {
							flows[idx] = flow
							idx++
						}
					}
					start += int64(len(res))
				} else {
					break
				}
			}
		}
		return flows, nil
	}
}

func (r *RedisStorage) RangeFlows(ctx context.Context, coin string, handle func([]*Flow) (stop bool)) {
	total, err := r.client.LLen(r.noCtx, coin).Result()
	if err != nil {
		log.Error.Println(err)
	} else {
		var start int64 = 0
		var limit int64 = 500
		for {
			select {
			case <-ctx.Done():
				return
			default:
				stop := start + limit - 1
				if stop >= total {
					stop = total - 1
				}
				res, err := r.client.LRange(r.noCtx, coin, start, stop).Result()
				if err != nil && err != redis.Nil {
					log.Error.Println(err)
				} else {
					if len(res) > 0 {
						var flows = make([]*Flow, len(res))
						for i, re := range res {
							flow := &Flow{}
							err = json.Unmarshal([]byte(re), flow)
							if err != nil {
								log.Error.Println(err)
								return
							} else {
								flows[i] = flow
							}
						}
						if handle(flows) {
							return
						}
						start += int64(len(res))
					} else {
						return
					}
				}
			}
		}
	}
}
